1   /*
2    * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.nio.ch;
27  
28  import java.io.*;
29  import java.net.*;
30  import java.nio.*;
31  import java.nio.channels.*;
32  
33  
34  // Make a datagram-socket channel look like a datagram socket.
35  //
36  // The methods in this class are defined in exactly the same order as in
37  // java.net.DatagramSocket so as to simplify tracking future changes to that
38  // class.
39  //
40  
41  public class DatagramSocketAdaptor
42      extends DatagramSocket
43  {
44  
45      // The channel being adapted
46      private final DatagramChannelImpl dc;
47  
48      // Timeout "option" value for receives
49      private volatile int timeout = 0;
50  
51      // ## super will create a useless impl
52      private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
53          // Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
54          // passing a dummy DatagramSocketImpl object to aovid any native
55          // resource allocation in super class and invoking our bind method
56          // before the dc field is initialized.
57          super(dummyDatagramSocket);
58          this.dc = dc;
59      }
60  
61      public static DatagramSocket create(DatagramChannelImpl dc) {
62          try {
63              return new DatagramSocketAdaptor(dc);
64          } catch (IOException x) {
65              throw new Error(x);
66          }
67      }
68  
69      private void connectInternal(SocketAddress remote)
70          throws SocketException
71      {
72          InetSocketAddress isa = Net.asInetSocketAddress(remote);
73          int port = isa.getPort();
74          if (port < 0 || port > 0xFFFF)
75              throw new IllegalArgumentException("connect: " + port);
76          if (remote == null)
77              throw new IllegalArgumentException("connect: null address");
78          if (isClosed())
79              return;
80          try {
81              dc.connect(remote);
82          } catch (Exception x) {
83              Net.translateToSocketException(x);
84          }
85      }
86  
87      public void bind(SocketAddress local) throws SocketException {
88          try {
89              if (local == null)
90                  local = new InetSocketAddress(0);
91              dc.bind(local);
92          } catch (Exception x) {
93              Net.translateToSocketException(x);
94          }
95      }
96  
97      public void connect(InetAddress address, int port) {
98          try {
99              connectInternal(new InetSocketAddress(address, port));
100         } catch (SocketException x) {
101             // Yes, j.n.DatagramSocket really does this
102         }
103     }
104 
105     public void connect(SocketAddress remote) throws SocketException {
106         if (remote == null)
107             throw new IllegalArgumentException("Address can't be null");
108         connectInternal(remote);
109     }
110 
111     public void disconnect() {
112         try {
113             dc.disconnect();
114         } catch (IOException x) {
115             throw new Error(x);
116         }
117     }
118 
119     public boolean isBound() {
120         return dc.localAddress() != null;
121     }
122 
123     public boolean isConnected() {
124         return dc.remoteAddress() != null;
125     }
126 
127     public InetAddress getInetAddress() {
128         return (isConnected()
129                 ? Net.asInetSocketAddress(dc.remoteAddress()).getAddress()
130                 : null);
131     }
132 
133     public int getPort() {
134         return (isConnected()
135                 ? Net.asInetSocketAddress(dc.remoteAddress()).getPort()
136                 : -1);
137     }
138 
139     public void send(DatagramPacket p) throws IOException {
140         synchronized (dc.blockingLock()) {
141             if (!dc.isBlocking())
142                 throw new IllegalBlockingModeException();
143             try {
144                 synchronized (p) {
145                     ByteBuffer bb = ByteBuffer.wrap(p.getData(),
146                                                     p.getOffset(),
147                                                     p.getLength());
148                     if (dc.isConnected()) {
149                         if (p.getAddress() == null) {
150                             // Legacy DatagramSocket will send in this case
151                             // and set address and port of the packet
152                             InetSocketAddress isa = (InetSocketAddress)
153                                                     dc.remoteAddress();
154                             p.setPort(isa.getPort());
155                             p.setAddress(isa.getAddress());
156                             dc.write(bb);
157                         } else {
158                             // Target address may not match connected address
159                             dc.send(bb, p.getSocketAddress());
160                         }
161                     } else {
162                         // Not connected so address must be valid or throw
163                         dc.send(bb, p.getSocketAddress());
164                     }
165                 }
166             } catch (IOException x) {
167                 Net.translateException(x);
168             }
169         }
170     }
171 
172     // Must hold dc.blockingLock()
173     //
174     private SocketAddress receive(ByteBuffer bb) throws IOException {
175         if (timeout == 0) {
176             return dc.receive(bb);
177         }
178 
179         // Implement timeout with a selector
180         SelectionKey sk = null;
181         Selector sel = null;
182         dc.configureBlocking(false);
183         try {
184             int n;
185             SocketAddress sender;
186             if ((sender = dc.receive(bb)) != null)
187                 return sender;
188             sel = Util.getTemporarySelector(dc);
189             sk = dc.register(sel, SelectionKey.OP_READ);
190             long to = timeout;
191             for (;;) {
192                 if (!dc.isOpen())
193                      throw new ClosedChannelException();
194                 long st = System.currentTimeMillis();
195                 int ns = sel.select(to);
196                 if (ns > 0 && sk.isReadable()) {
197                     if ((sender = dc.receive(bb)) != null)
198                         return sender;
199                 }
200                 sel.selectedKeys().remove(sk);
201                 to -= System.currentTimeMillis() - st;
202                 if (to <= 0)
203                     throw new SocketTimeoutException();
204 
205             }
206         } finally {
207             if (sk != null)
208                 sk.cancel();
209             if (dc.isOpen())
210                 dc.configureBlocking(true);
211             if (sel != null)
212                 Util.releaseTemporarySelector(sel);
213         }
214     }
215 
216     public void receive(DatagramPacket p) throws IOException {
217         synchronized (dc.blockingLock()) {
218             if (!dc.isBlocking())
219                 throw new IllegalBlockingModeException();
220             try {
221                 synchronized (p) {
222                     ByteBuffer bb = ByteBuffer.wrap(p.getData(),
223                                                     p.getOffset(),
224                                                     p.getLength());
225                     SocketAddress sender = receive(bb);
226                     p.setSocketAddress(sender);
227                     p.setLength(bb.position() - p.getOffset());
228                 }
229             } catch (IOException x) {
230                 Net.translateException(x);
231             }
232         }
233     }
234 
235     public InetAddress getLocalAddress() {
236         if (isClosed())
237             return null;
238         SocketAddress local = dc.localAddress();
239         if (local == null)
240             local = new InetSocketAddress(0);
241         InetAddress result = ((InetSocketAddress)local).getAddress();
242         SecurityManager sm = System.getSecurityManager();
243         if (sm != null) {
244             try {
245                 sm.checkConnect(result.getHostAddress(), -1);
246             } catch (SecurityException x) {
247                 return new InetSocketAddress(0).getAddress();
248             }
249         }
250         return result;
251     }
252 
253     public int getLocalPort() {
254         if (isClosed())
255             return -1;
256         try {
257             SocketAddress local = dc.getLocalAddress();
258             if (local != null) {
259                 return ((InetSocketAddress)local).getPort();
260             }
261         } catch (Exception x) {
262         }
263         return 0;
264     }
265 
266     public void setSoTimeout(int timeout) throws SocketException {
267         this.timeout = timeout;
268     }
269 
270     public int getSoTimeout() throws SocketException {
271         return timeout;
272     }
273 
274     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
275         throws SocketException
276     {
277         try {
278             dc.setOption(name, value);
279         } catch (IOException x) {
280             Net.translateToSocketException(x);
281         }
282     }
283 
284     private void setIntOption(SocketOption<Integer> name, int value)
285         throws SocketException
286     {
287         try {
288             dc.setOption(name, value);
289         } catch (IOException x) {
290             Net.translateToSocketException(x);
291         }
292     }
293 
294     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
295         try {
296             return dc.getOption(name).booleanValue();
297         } catch (IOException x) {
298             Net.translateToSocketException(x);
299             return false;       // keep compiler happy
300         }
301     }
302 
303     private int getIntOption(SocketOption<Integer> name) throws SocketException {
304         try {
305             return dc.getOption(name).intValue();
306         } catch (IOException x) {
307             Net.translateToSocketException(x);
308             return -1;          // keep compiler happy
309         }
310     }
311 
312     public void setSendBufferSize(int size) throws SocketException {
313         if (size <= 0)
314             throw new IllegalArgumentException("Invalid send size");
315         setIntOption(StandardSocketOptions.SO_SNDBUF, size);
316     }
317 
318     public int getSendBufferSize() throws SocketException {
319         return getIntOption(StandardSocketOptions.SO_SNDBUF);
320     }
321 
322     public void setReceiveBufferSize(int size) throws SocketException {
323         if (size <= 0)
324             throw new IllegalArgumentException("Invalid receive size");
325         setIntOption(StandardSocketOptions.SO_RCVBUF, size);
326     }
327 
328     public int getReceiveBufferSize() throws SocketException {
329         return getIntOption(StandardSocketOptions.SO_RCVBUF);
330     }
331 
332     public void setReuseAddress(boolean on) throws SocketException {
333         setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
334     }
335 
336     public boolean getReuseAddress() throws SocketException {
337         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
338 
339     }
340 
341     public void setBroadcast(boolean on) throws SocketException {
342         setBooleanOption(StandardSocketOptions.SO_BROADCAST, on);
343     }
344 
345     public boolean getBroadcast() throws SocketException {
346         return getBooleanOption(StandardSocketOptions.SO_BROADCAST);
347     }
348 
349     public void setTrafficClass(int tc) throws SocketException {
350         setIntOption(StandardSocketOptions.IP_TOS, tc);
351     }
352 
353     public int getTrafficClass() throws SocketException {
354         return getIntOption(StandardSocketOptions.IP_TOS);
355     }
356 
357     public void close() {
358         try {
359             dc.close();
360         } catch (IOException x) {
361             throw new Error(x);
362         }
363     }
364 
365     public boolean isClosed() {
366         return !dc.isOpen();
367     }
368 
369     public DatagramChannel getChannel() {
370         return dc;
371     }
372 
373    /*
374     * A dummy implementation of DatagramSocketImpl that can be passed to the
375     * DatagramSocket constructor so that no native resources are allocated in
376     * super class.
377     */
378    private static final DatagramSocketImpl dummyDatagramSocket
379        = new DatagramSocketImpl()
380    {
381        protected void create() throws SocketException {}
382 
383        protected void bind(int lport, InetAddress laddr) throws SocketException {}
384 
385        protected void send(DatagramPacket p) throws IOException {}
386 
387        protected int peek(InetAddress i) throws IOException { return 0; }
388 
389        protected int peekData(DatagramPacket p) throws IOException { return 0; }
390 
391        protected void receive(DatagramPacket p) throws IOException {}
392 
393        protected void setTTL(byte ttl) throws IOException {}
394 
395        protected byte getTTL() throws IOException { return 0; }
396 
397        protected void setTimeToLive(int ttl) throws IOException {}
398 
399        protected int getTimeToLive() throws IOException { return 0;}
400 
401        protected void join(InetAddress inetaddr) throws IOException {}
402 
403        protected void leave(InetAddress inetaddr) throws IOException {}
404 
405        protected void joinGroup(SocketAddress mcastaddr,
406                                  NetworkInterface netIf) throws IOException {}
407 
408        protected void leaveGroup(SocketAddress mcastaddr,
409                                  NetworkInterface netIf) throws IOException {}
410 
411        protected void close() {}
412 
413        public Object getOption(int optID) throws SocketException { return null;}
414 
415        public void setOption(int optID, Object value) throws SocketException {}
416    };
417 }